use std::any::Any;
use std::sync::Arc;
use std::time::Duration;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use protoactor_rs::actor::{Actor, ActorContext, ActorError, DefaultActorContext, PID, Props};
use protoactor_rs::persistence::{EventStore, InMemoryEventStore, PersistenceError, PersistentActor, PersistentEvent, PersistentState};
use protoactor_rs::system::ActorSystem;

// Messages for our counter actor
enum CounterMessage {
    Increment(i32),
    Decrement(i32),
    Get,
    Reset,
}

// Events for persistence
#[derive(Debug, Clone, Serialize, Deserialize)]
enum CounterEvent {
    Incremented(i32),
    Decremented(i32),
    Reset,
}

impl PersistentEvent for CounterEvent {
    fn event_type(&self) -> &str {
        match self {
            CounterEvent::Incremented(_) => "incremented",
            CounterEvent::Decremented(_) => "decremented",
            CounterEvent::Reset => "reset",
        }
    }
}

// State for snapshots
#[derive(Debug, Clone, Serialize, Deserialize)]
struct CounterState {
    count: i32,
    version: u64,
}

impl PersistentState for CounterState {
    fn version(&self) -> u64 {
        self.version
    }
    
    fn set_version(&mut self, version: u64) {
        self.version = version;
    }
}

// Our persistent counter actor
struct CounterActor {
    count: i32,
}

impl CounterActor {
    fn new() -> Self {
        Self { count: 0 }
    }
}

#[async_trait]
impl PersistentActor for CounterActor {
    type Event = CounterEvent;
    type State = CounterState;
    type Context = DefaultActorContext;
    
    fn apply_event(&mut self, event: &Self::Event) {
        match event {
            CounterEvent::Incremented(value) => self.count += value,
            CounterEvent::Decremented(value) => self.count -= value,
            CounterEvent::Reset => self.count = 0,
        }
    }
    
    fn apply_snapshot(&mut self, snapshot: &Self::State) {
        self.count = snapshot.count;
    }
    
    fn get_state(&self) -> Self::State {
        CounterState {
            count: self.count,
            version: 0, // Will be set by the context
        }
    }
    
    async fn handle_message(&mut self, context: &mut Self::Context, message: Box<dyn Any + Send>) -> Result<(), ActorError> {
        if let Some(msg) = message.downcast_ref::<CounterMessage>() {
            match msg {
                CounterMessage::Increment(value) => {
                    // Create and persist the event
                    let event = CounterEvent::Incremented(*value);
                    // In a real implementation, we would call a persist method here
                    // self.persist_event(context, event).await?;
                    
                    // For this example, just apply it directly
                    self.apply_event(&event);
                    println!("Counter incremented by {}. New value: {}", value, self.count);
                }
                CounterMessage::Decrement(value) => {
                    let event = CounterEvent::Decremented(*value);
                    // self.persist_event(context, event).await?;
                    self.apply_event(&event);
                    println!("Counter decremented by {}. New value: {}", value, self.count);
                }
                CounterMessage::Reset => {
                    let event = CounterEvent::Reset;
                    // self.persist_event(context, event).await?;
                    self.apply_event(&event);
                    println!("Counter reset to 0");
                }
                CounterMessage::Get => {
                    println!("Current counter value: {}", self.count);
                    context.respond(self.count);
                }
            }
        }
        Ok(())
    }
    
    async fn on_recovery_complete(&mut self, _context: &mut Self::Context) -> Result<(), ActorError> {
        println!("Recovery complete. Counter value: {}", self.count);
        Ok(())
    }
}

// Function to spawn a persistent counter
async fn spawn_persistent_counter(
    system: &ActorSystem,
    store: Arc<dyn EventStore<Event = CounterEvent, State = CounterState>>,
    actor_id: &str,
) -> Result<PID, ActorError> {
    // Create the actor
    let actor = CounterActor::new();
    
    // Create props (in a real implementation, this would be specialized for persistent actors)
    let props = Props::from_producer(move || actor);
    
    // Spawn the actor
    let pid = system.root_context().spawn(props).await;
    
    // In a real implementation, we would register the actor with the persistence system here
    // and perform initial recovery
    
    println!("Spawned persistent counter with ID: {}", actor_id);
    Ok(pid)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize the actor system
    let system = ActorSystem::new().await;
    
    // Create an in-memory event store
    let store = Arc::new(InMemoryEventStore::<CounterEvent, CounterState>::new(10));
    
    // Spawn our persistent counter
    let counter_pid = spawn_persistent_counter(&system, store.clone(), "counter-1").await?;
    
    // Send some messages to the counter
    system.root_context().send(&counter_pid, CounterMessage::Get).await?;
    system.root_context().send(&counter_pid, CounterMessage::Increment(5)).await?;
    system.root_context().send(&counter_pid, CounterMessage::Get).await?;
    system.root_context().send(&counter_pid, CounterMessage::Decrement(2)).await?;
    system.root_context().send(&counter_pid, CounterMessage::Get).await?;
    system.root_context().send(&counter_pid, CounterMessage::Reset).await?;
    system.root_context().send(&counter_pid, CounterMessage::Get).await?;
    
    // Give some time for messages to be processed
    tokio::time::sleep(Duration::from_millis(100)).await;
    
    // In a real scenario, we would stop the actor, restart it, and see the state recovered
    
    println!("Shutting down system");
    system.shutdown().await?;
    
    Ok(())
} 